/**
 * 集合模块 - 适用于数组和对象的通用集合操作
 * 借鉴Lodash的Collection理念
 */

/**
 * 检查集合中的所有元素是否都满足条件
 * @param {Array|Object} collection 集合
 * @param {Function} predicate 条件函数
 * @returns {boolean} 是否都满足
 */
export function every(collection, predicate) {
  if (!collection) return true;
  if (typeof predicate !== 'function') return true;

  if (Array.isArray(collection)) {
    return collection.every(predicate);
  }

  if (typeof collection === 'object') {
    return Object.keys(collection).every(key => predicate(collection[key], key, collection));
  }

  return true;
}

/**
 * 检查集合中是否有元素满足条件
 * @param {Array|Object} collection 集合
 * @param {Function} predicate 条件函数
 * @returns {boolean} 是否有满足的
 */
export function some(collection, predicate) {
  if (!collection) return false;
  if (typeof predicate !== 'function') return false;

  if (Array.isArray(collection)) {
    return collection.some(predicate);
  }

  if (typeof collection === 'object') {
    return Object.keys(collection).some(key => predicate(collection[key], key, collection));
  }

  return false;
}

/**
 * 检查集合中是否包含指定值
 * @param {Array|Object|string} collection 集合
 * @param {any} value 要查找的值
 * @param {number} fromIndex 起始索引（仅数组/字符串）
 * @returns {boolean} 是否包含
 */
export function includes(collection, value, fromIndex = 0) {
  if (!collection) return false;

  if (Array.isArray(collection)) {
    return collection.slice(fromIndex).includes(value);
  }

  if (typeof collection === 'string') {
    return collection.slice(fromIndex).includes(value);
  }

  if (typeof collection === 'object') {
    return Object.values(collection).includes(value);
  }

  return false;
}

/**
 * 遍历集合的每个元素
 * @param {Array|Object} collection 集合
 * @param {Function} iteratee 迭代函数
 * @returns {Array|Object} 原集合
 */
export function forEach(collection, iteratee) {
  if (!collection || typeof iteratee !== 'function') return collection;

  if (Array.isArray(collection)) {
    collection.forEach(iteratee);
  } else if (typeof collection === 'object') {
    Object.keys(collection).forEach(key => {
      iteratee(collection[key], key, collection);
    });
  }

  return collection;
}

/**
 * 映射集合的每个元素
 * @param {Array|Object} collection 集合
 * @param {Function} iteratee 映射函数
 * @returns {Array} 映射后的数组
 */
export function map(collection, iteratee) {
  if (!collection) return [];
  if (typeof iteratee !== 'function') iteratee = (x) => x;

  if (Array.isArray(collection)) {
    return collection.map(iteratee);
  }

  if (typeof collection === 'object') {
    return Object.keys(collection).map(key => iteratee(collection[key], key, collection));
  }

  return [];
}

/**
 * 过滤集合
 * @param {Array|Object} collection 集合
 * @param {Function} predicate 过滤条件
 * @returns {Array} 过滤后的数组
 */
export function filter(collection, predicate) {
  if (!collection) return [];
  if (typeof predicate !== 'function') return [];

  if (Array.isArray(collection)) {
    return collection.filter(predicate);
  }

  if (typeof collection === 'object') {
    const result = [];
    Object.keys(collection).forEach(key => {
      if (predicate(collection[key], key, collection)) {
        result.push(collection[key]);
      }
    });
    return result;
  }

  return [];
}

/**
 * 反向过滤（排除满足条件的元素）
 * @param {Array|Object} collection 集合
 * @param {Function} predicate 过滤条件
 * @returns {Array} 过滤后的数组
 */
export function reject(collection, predicate) {
  if (!collection) return [];
  if (typeof predicate !== 'function') return [];

  return filter(collection, (...args) => !predicate(...args));
}

/**
 * 归约集合
 * @param {Array|Object} collection 集合
 * @param {Function} iteratee 归约函数
 * @param {any} accumulator 初始值
 * @returns {any} 归约结果
 */
export function reduce(collection, iteratee, accumulator) {
  if (!collection || typeof iteratee !== 'function') return accumulator;

  if (Array.isArray(collection)) {
    return accumulator === undefined
      ? collection.reduce(iteratee)
      : collection.reduce(iteratee, accumulator);
  }

  if (typeof collection === 'object') {
    let result = accumulator;
    let isFirst = result === undefined;

    Object.keys(collection).forEach(key => {
      if (isFirst) {
        result = collection[key];
        isFirst = false;
      } else {
        result = iteratee(result, collection[key], key, collection);
      }
    });

    return result;
  }

  return accumulator;
}

/**
 * 从右向左归约集合
 * @param {Array|Object} collection 集合
 * @param {Function} iteratee 归约函数
 * @param {any} accumulator 初始值
 * @returns {any} 归约结果
 */
export function reduceRight(collection, iteratee, accumulator) {
  if (!collection || typeof iteratee !== 'function') return accumulator;

  if (Array.isArray(collection)) {
    return accumulator === undefined
      ? collection.reduceRight(iteratee)
      : collection.reduceRight(iteratee, accumulator);
  }

  if (typeof collection === 'object') {
    const keys = Object.keys(collection).reverse();
    let result = accumulator;
    let isFirst = result === undefined;

    keys.forEach(key => {
      if (isFirst) {
        result = collection[key];
        isFirst = false;
      } else {
        result = iteratee(result, collection[key], key, collection);
      }
    });

    return result;
  }

  return accumulator;
}

/**
 * 查找满足条件的第一个元素
 * @param {Array|Object} collection 集合
 * @param {Function} predicate 查找条件
 * @param {number} fromIndex 起始索引（仅数组）
 * @returns {any} 找到的元素
 */
export function find(collection, predicate, fromIndex = 0) {
  if (!collection || typeof predicate !== 'function') return undefined;

  if (Array.isArray(collection)) {
    for (let i = fromIndex; i < collection.length; i++) {
      if (predicate(collection[i], i, collection)) {
        return collection[i];
      }
    }
    return undefined;
  }

  if (typeof collection === 'object') {
    const keys = Object.keys(collection);
    for (const key of keys) {
      if (predicate(collection[key], key, collection)) {
        return collection[key];
      }
    }
  }

  return undefined;
}

/**
 * 从右向左查找满足条件的第一个元素
 * @param {Array|Object} collection 集合
 * @param {Function} predicate 查找条件
 * @param {number} fromIndex 起始索引（仅数组）
 * @returns {any} 找到的元素
 */
export function findLast(collection, predicate, fromIndex) {
  if (!collection || typeof predicate !== 'function') return undefined;

  if (Array.isArray(collection)) {
    const startIndex = fromIndex === undefined ? collection.length - 1 : fromIndex;
    for (let i = startIndex; i >= 0; i--) {
      if (predicate(collection[i], i, collection)) {
        return collection[i];
      }
    }
    return undefined;
  }

  if (typeof collection === 'object') {
    const keys = Object.keys(collection).reverse();
    for (const key of keys) {
      if (predicate(collection[key], key, collection)) {
        return collection[key];
      }
    }
  }

  return undefined;
}

/**
 * 获取集合的大小
 * @param {Array|Object|string} collection 集合
 * @returns {number} 大小
 */
export function size(collection) {
  if (!collection) return 0;

  if (Array.isArray(collection) || typeof collection === 'string') {
    return collection.length;
  }

  if (typeof collection === 'object') {
    return Object.keys(collection).length;
  }

  return 0;
}

/**
 * 按条件分组
 * @param {Array|Object} collection 集合
 * @param {string|Function} iteratee 分组条件
 * @returns {Object} 分组后的对象
 */
export function groupBy(collection, iteratee) {
  if (!collection) return {};

  const getKey = typeof iteratee === 'function'
    ? iteratee
    : (item) => item[iteratee];

  const result = {};

  if (Array.isArray(collection)) {
    collection.forEach((item, index) => {
      const key = getKey(item, index, collection);
      if (!result[key]) result[key] = [];
      result[key].push(item);
    });
  } else if (typeof collection === 'object') {
    Object.keys(collection).forEach(key => {
      const groupKey = getKey(collection[key], key, collection);
      if (!result[groupKey]) result[groupKey] = [];
      result[groupKey].push(collection[key]);
    });
  }

  return result;
}

/**
 * 按键索引
 * @param {Array|Object} collection 集合
 * @param {string|Function} iteratee 键生成函数
 * @returns {Object} 索引对象
 */
export function keyBy(collection, iteratee) {
  if (!collection) return {};

  const getKey = typeof iteratee === 'function'
    ? iteratee
    : (item) => item[iteratee];

  const result = {};

  if (Array.isArray(collection)) {
    collection.forEach((item, index) => {
      const key = getKey(item, index, collection);
      result[key] = item;
    });
  } else if (typeof collection === 'object') {
    Object.keys(collection).forEach(key => {
      const itemKey = getKey(collection[key], key, collection);
      result[itemKey] = collection[key];
    });
  }

  return result;
}

/**
 * 按条件统计
 * @param {Array|Object} collection 集合
 * @param {string|Function} iteratee 统计条件
 * @returns {Object} 统计结果
 */
export function countBy(collection, iteratee) {
  if (!collection) return {};

  const getKey = typeof iteratee === 'function'
    ? iteratee
    : (item) => item[iteratee];

  const result = {};

  if (Array.isArray(collection)) {
    collection.forEach((item, index) => {
      const key = getKey(item, index, collection);
      result[key] = (result[key] || 0) + 1;
    });
  } else if (typeof collection === 'object') {
    Object.keys(collection).forEach(key => {
      const countKey = getKey(collection[key], key, collection);
      result[countKey] = (result[countKey] || 0) + 1;
    });
  }

  return result;
}

/**
 * 按条件排序
 * @param {Array|Object} collection 集合
 * @param {string[]|Function[]} iteratees 排序条件
 * @param {string[]} orders 排序方向
 * @returns {Array} 排序后的数组
 */
export function orderBy(collection, iteratees = [], orders = []) {
  if (!collection) return [];

  const items = Array.isArray(collection)
    ? collection.slice()
    : Object.values(collection);

  if (!Array.isArray(iteratees)) iteratees = [iteratees];
  if (!Array.isArray(orders)) orders = [orders];

  return items.sort((a, b) => {
    for (let i = 0; i < iteratees.length; i++) {
      const iteratee = iteratees[i];
      const order = orders[i] || 'asc';

      const aVal = typeof iteratee === 'function' ? iteratee(a) : a[iteratee];
      const bVal = typeof iteratee === 'function' ? iteratee(b) : b[iteratee];

      if (aVal < bVal) return order === 'desc' ? 1 : -1;
      if (aVal > bVal) return order === 'desc' ? -1 : 1;
    }
    return 0;
  });
}

/**
 * 按条件分区
 * @param {Array|Object} collection 集合
 * @param {Function} predicate 分区条件
 * @returns {Array} [满足条件的数组, 不满足条件的数组]
 */
export function partition(collection, predicate) {
  if (!collection || typeof predicate !== 'function') return [[], []];

  const truthy = [];
  const falsy = [];

  if (Array.isArray(collection)) {
    collection.forEach((item, index) => {
      if (predicate(item, index, collection)) {
        truthy.push(item);
      } else {
        falsy.push(item);
      }
    });
  } else if (typeof collection === 'object') {
    Object.keys(collection).forEach(key => {
      if (predicate(collection[key], key, collection)) {
        truthy.push(collection[key]);
      } else {
        falsy.push(collection[key]);
      }
    });
  }

  return [truthy, falsy];
}

/**
 * 随机取样
 * @param {Array|Object} collection 集合
 * @param {number} n 取样数量
 * @returns {any|Array} 取样结果
 */
export function sample(collection, n) {
  if (!collection) return n === undefined ? undefined : [];

  const items = Array.isArray(collection)
    ? collection
    : Object.values(collection);

  if (items.length === 0) return n === undefined ? undefined : [];

  // 单个样本
  if (n === undefined) {
    const randomIndex = Math.floor(Math.random() * items.length);
    return items[randomIndex];
  }

  // 多个样本
  if (n <= 0) return [];
  if (n >= items.length) {
    // 洗牌
    const shuffled = items.slice();
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled;
  }

  const result = [];
  const used = new Set();

  while (result.length < n) {
    const index = Math.floor(Math.random() * items.length);
    if (!used.has(index)) {
      used.add(index);
      result.push(items[index]);
    }
  }

  return result;
}

/**
 * 洗牌
 * @param {Array|Object} collection 集合
 * @returns {Array} 洗牌后的数组
 */
export function shuffle(collection) {
  if (!collection) return [];

  const items = Array.isArray(collection)
    ? collection.slice()
    : Object.values(collection);

  for (let i = items.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [items[i], items[j]] = [items[j], items[i]];
  }

  return items;
}

/**
 * 映射并扁平化
 * @param {Array|Object} collection 集合
 * @param {Function} iteratee 映射函数
 * @returns {Array} 扁平化后的数组
 */
export function flatMap(collection, iteratee) {
  if (!collection) return [];

  const fn = typeof iteratee === 'function' ? iteratee : (x) => x;
  const mapped = map(collection, fn);

  return mapped.reduce((acc, item) => {
    if (Array.isArray(item)) {
      acc.push(...item);
    } else {
      acc.push(item);
    }
    return acc;
  }, []);
}

/**
 * 映射并深度扁平化
 * @param {Array|Object} collection 集合
 * @param {Function} iteratee 映射函数
 * @returns {Array} 扁平化后的数组
 */
export function flatMapDeep(collection, iteratee) {
  if (!collection) return [];

  const fn = typeof iteratee === 'function' ? iteratee : (x) => x;
  const mapped = map(collection, fn);

  function flattenDeep(arr) {
    return arr.reduce((acc, item) => {
      if (Array.isArray(item)) {
        acc.push(...flattenDeep(item));
      } else {
        acc.push(item);
      }
      return acc;
    }, []);
  }

  return flattenDeep(mapped);
}

/**
 * 调用每个元素的方法
 * @param {Array|Object} collection 集合
 * @param {string} path 方法路径
 * @param {...any} args 参数
 * @returns {Array} 调用结果数组
 */
export function invokeMap(collection, path, ...args) {
  if (!collection) return [];

  return map(collection, (item) => {
    if (typeof path === 'function') {
      return path.apply(item, args);
    }

    const method = typeof path === 'string' && item[path];
    if (typeof method === 'function') {
      return method.apply(item, args);
    }

    return undefined;
  });
}

/**
 * 将集合转换为数组
 * @param {Array|Object|string} collection 集合
 * @returns {Array} 数组
 */
export function toArray(collection) {
  if (!collection) return [];

  if (Array.isArray(collection)) return collection.slice();
  if (typeof collection === 'string') return collection.split('');
  if (typeof collection === 'object') return Object.values(collection);

  return [];
}
